package com.fast.security.config;

import com.fast.configs.properties.AuthorizationProperties;
import com.fast.security.AuthenticationEntryPointImpl;
import com.fast.security.TokenAuthenticationFilter;
import com.fast.security.UserDetailsServiceImpl;
import com.fast.security.handler.AccessDeniedHandlerImpl;
import com.fast.security.handler.AuthenticationFailureHandlerImpl;
import com.fast.security.handler.AuthenticationSuccessHandlerImpl;
import com.fast.security.handler.LogoutHandlerImpl;
import com.fast.utils.redis.RedisCache;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.logout.LogoutFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import javax.annotation.Resource;
import java.util.Collections;

/**
 * @author ruan cai yuan
 * @version 1.0
 * @fileName com.fast.security.config.SecurityConfig
 * @description: Security的配置类
 * @since 2024/6/23 14:27
 */
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Resource
    private AuthenticationSuccessHandlerImpl authenticationSuccessHandlerImpl;
    @Resource
    private AuthenticationFailureHandlerImpl authenticationFailureHandlerImpl;
    @Resource
    private AuthenticationEntryPointImpl authenticationEntryPoint;
    @Resource
    private LogoutHandlerImpl logoutHandler;
    @Resource
    private AccessDeniedHandlerImpl accessDeniedHandler;
    @Resource
    private AuthorizationProperties authorizationProperties;
    @Resource
    private UserDetailsServiceImpl userDetailsService;
    @Resource
    private RedisCache redisCache;

    //密码加载解析器
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public TokenAuthenticationFilter tokenAuthenticationFilter() {
        return new TokenAuthenticationFilter(redisCache);
    }

    /**
     * AuthenticationManagerBuilder用来构建AuthenticationManager, providerManager是AuthenticationManager接口的实现,
     * 持有多个AuthenticationProvider 用于认证传来的Authentication(UsernamePasswordAuthenticationFilter#attemptAuthentication中的UsernamePasswordAuthention中UsernamePasswordAuthenticationToken)),
     * 并返回认证结果Authentication，
     * DaoAuthenticationProvider是AuthenticationProvider的一个实现,用于调用  UserDetailsService.loadUserByUsername()
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }


    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 进制csrf
        http.csrf().disable()
                //配置跨域
                .cors().configurationSource(configurationSource())
                //基于token,不需要session
                .and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)

                //认证（登录）的相关配置 filter[UsernamePasswordAuthenticationFilter]
                //需要：username/password  不是这两个的话需要该成【本项目：userName/password】 提交方式表单提交
                .and().formLogin().usernameParameter("userName").passwordParameter("password")
                .loginProcessingUrl("/user/login")
                .successHandler(authenticationSuccessHandlerImpl)
                .failureHandler(authenticationFailureHandlerImpl)
                .and().addFilterBefore(tokenAuthenticationFilter(), LogoutFilter.class)

                //注销（登出） filter[LogoutFilter]
                .logout().logoutUrl("/user/logout").addLogoutHandler(logoutHandler)

                //授权配置
                .and().authorizeRequests().antMatchers(authorizationProperties.getWhiteList().toArray(new String[0])).permitAll()
                .antMatchers(HttpMethod.OPTIONS, "/**").permitAll().anyRequest()
                // .authenticated()   //到此即是登录即可访问所有的接口
                //  spel 参数可以传webSecurityExpressionRoot或其父类的字段
                .access("@rbacManager.hasPermission(request)")


                //异常处理（默认跳到登录页面）
                .and().exceptionHandling().authenticationEntryPoint(authenticationEntryPoint)
                //访问拒绝处理
                .accessDeniedHandler(accessDeniedHandler)

        ;
    }

    private CorsConfigurationSource configurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        //设置允许跨域的域名
        configuration.setAllowedOrigins(Collections.singletonList("*"));
        configuration.setAllowedHeaders(Collections.singletonList("*"));
        //post,get,options……
        configuration.setAllowedMethods(Collections.singletonList("*"));
        //单位s,用来设置options请求（预检请求）,在客户端缓存时间
        configuration.setMaxAge(3600L);
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        //设置允许跨域访问的url
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }


    public static void main(String[] args) {
        // 创建 BCryptPasswordEncoder 实例
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();

        // 要加密的原始密码
        String rawPassword = "admin";

        // 使用 BCryptPasswordEncoder 进行加密
        String encodedPassword = passwordEncoder.encode(rawPassword);

        // 输出加密后的密码
        System.out.println("原始密码: " + rawPassword);
        System.out.println("加密后的密码: " + encodedPassword);
    }
}
